/*
  FSKModem.cpp - Software serial library
  Copyright (c) 2010 Akihiro Uehara.  All right reserved.
*/
#include "FSKModem.h"

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

// gets pulse durations
#define FSK_CMP_THRESHOLD  10
#define MAX_PULSE_WIDTH    15
#define FSK_PERIOD_MID     5

/*
 * Constructor
 */
FSKModem::FSKModem(uint8_t receivePin, uint8_t transmitPin)
{
  _rx_pin     = receivePin;
  _tx_pin     = transmitPin;
  _is_heigh         = false;
  _carrier_detected = false;
  _rx_average = 0;
}

/*
 * Public methods
 */
void FSKModem::begin()
{
  // set ADC prescaler
  // 1/8 -> 16MHz, 13 cycle/sample -> 6.5usec/conversion
  sbi(ADCSRA,ADPS2) ;
  cbi(ADCSRA,ADPS1) ;
  cbi(ADCSRA,ADPS0) ;  

  // set pin mode
  pinMode(_tx_pin, OUTPUT);
  digitalWrite(_tx_pin, LOW);
}

// Asynchronous byte reader, 1 start-bit, 8 data-bit, 1 stop-bit, no parity bit.
// This function blocks until receives a byte.
// When it detects decoding error (illegal stop-bit and so on), it returns -1.
int FSKModem::read()
{
  int data;
  int period;
  
  // startbit ('0')
  while(true) {
/*
      //debug
    byte buf[32];
    for(int i = 0; i < 32; i++) {
      buf[i] = readPulse();
    }
    for(int i = 0; i < 32; i++) {
      Serial.print(buf[i], DEC);
      Serial.print(',');
    }
     Serial.print('\n');
*/

    period = readPulse();
    if( period == 2)
      return -1;

    if(period == 0)
      break;
  }

  // 8-bit data
  data = readByte();

  // stopbit  ('1')
  period = readPulse();  
  if(period != 1)
    return -1;

  return data;
}

void FSKModem::print(char data)
{
  // sync beats
  for(int i = 0; i < 16; i++)
    sendBit(1);

  // start bit '0'
  sendBit(0);

  // data
  for(int i = 0; i < 8 ; i++) {
    sendBit(data);
    data >>= 1;
  }

  // stop bit  '1'
  sendBit(1);

  // tail beats
  for(int i = 0; i < 4; i++)
    sendBit(1);
}

/*
 * Private methods
 */

// 1->narrow pulse, 0->wide pulse, else(2) -> no pulse, 
int FSKModem::readPulse()
{
  //debug
/*  
static int index=0;
static unsigned long int debug_buf[16];
*/
  int ret_val = 255;
  int amp, diff;
  unsigned long int cur_time, duration;
  // TODO put some code here to detect no-carrier condtion 
  //  while( period < MAX_PULSE_WIDTH ) {
  while(true) {
    // single-pole low pass filter r = 1/2^5 (time constant
    amp = analogRead(_rx_pin);
    cur_time = micros();
    
    diff = (amp << 5) - _rx_average;
    _rx_average += (diff >> 5); // time constant is 2^5=32 sampling time
    diff = (diff >> (5 + 2));   // 8-bit
    
    duration = (cur_time - _time); // in unit of 4usec
    
    if(_is_heigh) {
      // HIGH -> LOW?
      _is_heigh = (diff > 0);
    } else {
      // LOW -> HIGH?
      _is_heigh = (diff > FSK_CMP_THRESHOLD);
      if(_is_heigh ) {
        _time = cur_time;
        
        // pulse width decision
        if(_carrier_detected) {
          // get pulse width
          if(duration > 1300) {
            ret_val = 0;
          } else if(duration > 420) {
            ret_val = 1;
          } else {
            ret_val = 2;
          }
        }
        _carrier_detected = true;
      }
    }
    
    // time out
    if(duration > 2000) {
      _carrier_detected = false;
      _time = cur_time;
      ret_val = 2;
    }
    
    delayMicroseconds(104 - 6); // ADC 6usec
    
    if(ret_val != 255)
      return ret_val;
  }
}

int FSKModem::readByte()
{
  int data = 0;
  byte period;

  for(int index = 0; index < 8; index++) {
    data >>= 1;
    // get a pulse width
    period = readPulse();
    // is logic '0' or '1'?
    switch(period) {
    case 0:  break;
    case 1:  data |= 0x080; break;
    default: return -1;
    }
  }
  return data;
}

void FSKModem::sendBit(byte data)
{
  unsigned int delayTime = ((data & 0x01) == 0 ) ? (833 -7)/2 : (410-7)/2;

  digitalWrite(_tx_pin, HIGH);
  delayMicroseconds(delayTime);
  digitalWrite(_tx_pin, LOW);
  delayMicroseconds(delayTime);
}

